/*
    抽象格式化父类
    派生格式化子类
        消息，
        等级，
        时间，
        文件名，
        行号，
        线程id，
        日志器名，
        制表符，
        换行，
        其他
*/


#ifndef __M_FMT_H__
#define __M_FMT_H__

#include "util.hpp"
#include "level.hpp"
#include "message.hpp"
#include <iostream>
#include <string>
#include <ctime>
#include <vector>
#include <cassert>
#include <sstream>

using namespace std;

namespace log{
    class FormatItem{
        public:
            //用智能指针对FormatItem进行管理
            using ptr = shared_ptr<FormatItem>;
            //把lmsg中的信息取出来放到out中
            virtual void format(ostream &out, LogMsg &lmsg) = 0;
    };

    class MsgFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << lmsg._payload;
            }
    };

    class LevelFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << LogLevel::toString(lmsg._level);
            }
    };

    class TimeFormatItem : public FormatItem{
        public:
            TimeFormatItem(const string &fmt = "%Y/%m/%d-%H:%M:%S"):_time_fmt(fmt){

            }

            void format(ostream &out, LogMsg &lmsg) override{
                //时间结构体
                tm t;
                //将时间戳表示的时间存放到时间结构体中
                localtime_r(&lmsg._ctime, &t);
                char tmp[64];
                //将t按_time_fmt的格式存到tmp中
                strftime(tmp, 31, _time_fmt.c_str(), &t);
                out << tmp;
            }

        private:
            //进行时间转化
            string _time_fmt;
    };

    class FileFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << lmsg._file;
            }
    };

    class LineFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << lmsg._line;
            }
    };

    class ThreadFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << lmsg._tid;
            }
    };

    class LoggerFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << lmsg._logger;
            }
    };

    class TabFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << "\t";
            }
    };



    class NLFormatItem : public FormatItem{
        public:
            void format(ostream &out, LogMsg &lmsg) override{
                out << "\n";
            }
    };

    class OtherFormatItem : public FormatItem{
        public:
            OtherFormatItem(const string& str):_str(str){

            }

            void format(ostream &out, LogMsg &lmsg) override{
                out << _str;
            }

        private:
            string _str;
    };

    class Formatter{
    public:
        using ptr = shared_ptr<Formatter>;

        //1、对传入的lmsg进行解析，按格式在_items中构建格式化子类对象
        //2、格式化控制器Formatter调用toFormat()，将lmsg传入_items中的格式化子类对象
        //3、_items中的格式化子类对象调用自己的format()输出自己所负责的lmsg实际信息

        //规定格式：日期-年/月/日-时:分:秒 [线程id] [日志级别] [日志器名称] [文件名:行号] 日志消息
        // %d日期
		// %T缩进
		// %t线程id
		// %p日志级别
		// %c日志器名称
		// %f文件名
		// %l行号
		// %m日志消息
		// %n换行
        Formatter(const string& pattern = "%d-%Y/%m/%d-%H:%M:%S [%t] [%p] [%c] [%f:%l] %T%m%n"):_pattern(pattern){
            assert(parsePattern());
        }

        //对lmsg进行格式化
        void toFormat(ostream &out, LogMsg &lmsg){
            //逐个从lmsg中取出对应的信息
            for(auto& it: _items){
                //it是_items中的元素（智能指针）
                //it中有format纯虚函数
                //根据父类指针item指向的子类，调用子类的format把lmsg中的指定信息取出来放到指定的out中
                it->format(out, lmsg);
            }
        }

        //对lmsg进行格式化
        string toFormat(LogMsg &lmsg){
            //stirng的out输出流
            stringstream ss;
            //调用void format，把lmsg的信息提取到ss中，返回ss
            toFormat(ss, lmsg);
            return ss.str();
        }


    private:
        //从_pattern中解析内容，按格式存入_items
        bool parsePattern(){
            //1、对格式化规则字符串进行解析
            //  日期-年/月/日-时:分:秒 [线程id] [日志级别] [日志器名称] [文件名:行号] 日志消息
            //  xxxxx %d-%Y/%m/%d-%H:%M:%S [%t] [%p] [%c] [%f:%l] %T%m%n
            //    1.当遇到%，代表接下来的一个字符是格式化字符，否则是普通字符
            //    2.如果%接下来的字符仍然是%，那么代表这是一个%字符，否则是格式化字符
            //    3.格式化字符之后，一直到' '或']'之前，都是属于这个格式化字符的子格式
            //  重复之上直到结束
            // 实际步骤：
            //  1.key = nullptr, val = xxxxx 
            //  2.key = d, val = %H:%M:%S
            //  3.key = nullptr, val =  [
            //  4.key = t, val = nullptr
            //  5.key = nullptr, val = ] [
            //  6.key = p, val = nullptr
            //  7.key = nullptr, val = ] [
            //  8.key = p, val = nullptr
            vector<pair<string, string>> fmt_order;
            size_t pos = 0;
            string key, val;
            while(pos < _pattern.size()){
                
                //1.普通字符
                if (_pattern[pos] != '%'){
                    //普通字符直接作为other插入
                    val.push_back(_pattern[pos]);
                    pos++;
                    continue;
                }
                //2.双%   下一个字符存在，且下一个字符为'%'
                else if(pos+1<_pattern.size() && _pattern[pos+1] == '%'){
                    //插入'%'
                    val.push_back('%');
                    pos += 2;
                    continue;
                }
                //3.单%，格式化字符
                else{
                    //处理格式化字符之前，需要先把普通字符插入到fmt_order中
                    //当val不为空（储存了普通字符），才插入
                    if(!val.empty()){
                        fmt_order.push_back(make_pair("", val));
                        val.clear();
                    }

                    //注意：此时可能直接走到了字符串末尾，出错
                    if(pos==_pattern.size()){
                        cerr << "%之后没有对应的格式化字符" << endl;
                        return false;
                    }

                    //处理格式化字符
                    pos++;
                    key = _pattern[pos];
                    //判断格式化字符后面的字符，确定val
                    pos++;

                    //日期类    %d-%Y/%m/%d-%H:%M:%S
                    if (_pattern[pos] == '-'){
                        //指向子格式%H:%M:%S的起始位置
                        pos++;

                        //当字符存在且不遇到' ' 或 ']'，代表包含在同一格式，这里日期类后面没有']'，所以只用判断' '
                        while(pos < _pattern.size() && _pattern[pos] != ' '){
                            val += (_pattern[pos]);
                            pos++;
                        }

                        //结束条件只有两个：遍历完、找到后标记符
                        //当遍历完仍然没有遇到后标记符，出错
                        if(pos == _pattern.size() && _pattern[pos] != ' '){
                            cerr << "日期类子格式出错，未找到后标记符 ' ' ！" << endl;
                            return false;
                        }
                        

                    }


                    //文件类不需要单独写，因为他们是不同的类，分别构造
                    //  FileFormatItem>()、<LineFormatItem>();
                    // //文件类  %f:%l
                    // if(_pattern[pos] == ':'){
                    //     //指向子格式%l的起始位置
                    //     pos++;

                    //     //文件类只用判断']'
                    //     while(pos < _pattern.size() && _pattern[pos] != ']'){
                    //         val += (_pattern[pos]);
                    //         pos++;
                    //     }

                    //     if(pos == _pattern.size() && _pattern[pos] != ']'){
                    //         cerr << "文件类类子格式出错，未找到后标记符 ']' ！" << endl;
                    //         return false;
                    //     }
                    // }
                }

                //把获取的格式插入到数组fmt_order中
                fmt_order.push_back(make_pair(key, val));
                key.clear();
                val.clear();
            }

            //2、根据解析得到的数据创建对应的格式化子项对象，添加到_items数组中 -- 初始化_items的成员
            //遍历 vector<pair<string, string>> fmt_order，根据key和val创建对应的格式化子项对象，添加到_items数组中
            for(auto& it : fmt_order){
                _items.push_back(createItem(it.first, it.second));
            }
            return true;
        }

        //实例化指针数组_items中的元素对象
        //根据key创建不同的格式化子项对象，val是子项的构造函数所需要的格式，比如：TimeFormatItem("%H:%M:%S")，ThreadFormatItem()
        FormatItem::ptr createItem(const string& key, const string& val){
            if(key == "d") return make_shared<TimeFormatItem>(val);
            if(key == "t") return make_shared<ThreadFormatItem>();
            if(key == "c") return make_shared<LoggerFormatItem>();
            if(key == "f") return make_shared<FileFormatItem>();
            if(key == "l") return make_shared<LineFormatItem>();
            if(key == "p") return make_shared<LevelFormatItem>();
            if(key == "T") return make_shared<TabFormatItem>();
            if(key == "m") return make_shared<MsgFormatItem>();
            if(key == "n") return make_shared<NLFormatItem>();


            //普通格式返回other
            if(key.empty()) return make_shared<OtherFormatItem>(val);

            //其他格式报错
            cout << "格式无法识别 %" << key << endl;
            abort();
            return FormatItem::ptr();
        }

    private:
        string _pattern;    //格式化规格字符串模板
        vector<FormatItem::ptr> _items;     //格式化子类的指针数组，从_pattern中解析内容，按格式存入_items
    };
}

#endif